package controller

import (
	"errors"
	"fmt"
	"gin/baidu"
	"gin/baidu/pcs"
	"gin/baidu/pcs/share/pcsconfig"
	"gin/cache"
	"gin/common"
	"gin/model"
	"gin/response"
	"gin/selenium/chrome"
	"gin/service"
	"gin/validate"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
	"github.com/spf13/viper"
	teamones_helper "github.com/teamones-open/go-helper"
	"net/http"
	"os"
	"strings"
	"time"
)

// 获取百度网盘授权连接
func GetAuthorizeUrl(ctx *gin.Context) {
	response.Success(ctx, gin.H{"url": baidu.GetAuthorizeUrl()}, "")
}

// 百度盘回调授权码，写入数据库
func PanCallback(ctx *gin.Context) {
	code := ctx.Query("code")

	errBody := ""
	if len(code) <= 0 {
		errBody = "Authorize code does not exist."
	}

	// 请求获取token
	accessToken, err := baidu.GetAccessToken(code)
	if err != nil {
		errBody = err.Error()
	} else {
		// 获取当前授权码是哪个会员的账户信息
		userInfo, err := baidu.GetPanUserInfo(accessToken.AccessToken)

		if err != nil {
			errBody = err.Error()
		}

		// 添加或者更新网盘授权
		err = service.UpdatePanUserAuthorization(accessToken, userInfo)

		if err != nil {
			errBody = err.Error()
		} else {
			errBody = "授权成功"
		}
		// 创建文件夹
		createFoldParam := validate.CreatePanParam{
			Path:  viper.GetString("bcs.path"),
			Isdir: 1,
			Rtype: 0,
			Size:  0,
		}
		_, err = baidu.Create(accessToken.AccessToken, createFoldParam)

		ctx.HTML(http.StatusOK, "auth_results.html", gin.H{
			"title": errBody + " 当前页面请手动关闭！",
		})
	}
}

// 添加百度网盘账户，最好是vip
func AddPanUser(ctx *gin.Context) {
	var addPanUser validate.AddPanUserData

	err := ctx.ShouldBindJSON(&addPanUser)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	panUser := service.FindPanUserByBaiduName(addPanUser.BaiduName)

	if panUser.Id > 0 {
		// 存在该用户
		response.FailWithCode(ctx, nil, fmt.Sprintf("baidu user %s already bind to %d", panUser.BaiduName, panUser.UnionId), -4000011)
		return
	}

	resData, err := service.CreatePanUser(addPanUser)
	if err != nil {
		// 插入失败
		response.Fail(ctx, nil, err.Error())
		return
	}
	service.UpdatePanUserCookiesExpireAt(&resData)

	response.Success(ctx, gin.H{"data": resData}, "success")
}

// 更新百度网盘账户信息
func UpdatePanUser(ctx *gin.Context) {
	var updatePanUser validate.UpdatePanUserData

	err := ctx.ShouldBindJSON(&updatePanUser)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	resData, err := service.UpdatePanUser(updatePanUser)
	if err != nil {
		// 插入失败
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": resData}, "success")
}

// 更新所有网盘token
func refreshAllToken(ctx *gin.Context) {

}

// 更新所有网盘token
func RefreshToken(ctx *gin.Context) {
	unionId := common.GetUInt64FromContext(ctx, "user_id")
	// 获取当前账户 Access Token
	panUser, err := service.FindPanUserByUnionId(unionId)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 获取当前网盘容量
	newToken, err := baidu.RefreshToken(panUser.RefreshToken)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	panUser, err = service.UpdatePanUserAccessTokenInfo(newToken, &panUser)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": panUser}, "success")
}

// 获取用户指定网盘容量 todo 修改为stoken
func GetUserAppointPanVolume(ctx *gin.Context) {
	var filter validate.GetUserAppointPanIdFilter

	err := ctx.ShouldBindJSON(&filter)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 获取当前账户 Access Token
	accessToken, err := service.GetAccessTokenById(filter.Id)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 获取当前网盘容量
	volume, err := baidu.GetPanVolume(accessToken)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": volume}, "success")
}

// 创建文件夹 todo 修改为stoken调用
func CreatePan(ctx *gin.Context) {
	var param validate.CreatePanParam

	err := ctx.ShouldBindJSON(&param)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 获取当前账户 Access Token
	accessToken, err := service.GetAccessTokenById(param.Id)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 创建文件夹
	resData, err := baidu.Create(accessToken, param)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	// 创建本地pan file记录
	addFileV := validate.RecordPanFileParam{
		PanUserId:      param.Id,
		FsId:           resData.FSId,
		Md5:            resData.Md5,
		ServerFilename: resData.Name,
		Category:       resData.Category,
		Path:           resData.Path,
		Size:           resData.Size,
		Ctime:          resData.Ctime,
		Mtime:          resData.Mtime,
		IsDir:          resData.IsDir,
	}
	panFIle, err := service.RecordUploadFile(addFileV)

	response.Success(ctx, gin.H{"bd_data": resData, "pan_file": panFIle}, "success")
}

// 获取上传文件，预上传、分片上传、创建文件链接，返回上传目录地址 todo 修改为stoken调用
func GetCreatePanFileUrl(ctx *gin.Context) {

	unionId := common.GetUInt64FromContext(ctx, "user_id")

	panUser, err := service.FindPanUserByUnionId(unionId)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	if panUser.AccessToken == "" {
		response.Fail(ctx, nil, "Unauthorized user")
		return
	}

	// 获取获取上传文件，预上传、分片上传、创建文件链接，返回上传目录地址数据
	resData, err := baidu.GetCreatePanFileUrl(panUser.AccessToken)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	resData.Id = panUser.Id

	response.Success(ctx, gin.H{"data": resData}, "success")
}

// 获取文件列表 todo 修改为stoken调用
func GetUserAppointPanFileList(ctx *gin.Context) {
	var filter validate.GetUserAppointPanIdFilter

	err := ctx.ShouldBindJSON(&filter)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 获取当前账户 Access Token
	accessToken, err := service.GetAccessTokenById(filter.Id)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 拼接查询路径
	var basePath = viper.GetString("bcs.path")
	if filter.Path != "" {
		basePath += filter.Path
	}
	// 获取文件列表
	volume, err := baidu.GetPanFileListAll(accessToken, basePath, filter.Start, filter.Limit)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": volume}, "success")
}

// 更新用户Token，每天定时扫描，距离过期还有5天的自动用 refresh_token 来刷新
func RefreshAllToken(ctx *gin.Context) {

	// 获取当前账户 Access Token
	resData, err := service.RefreshAllToken()

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": resData}, "success")
}

// 上传完记录上传的文件信息
func RecordUploadFile(ctx *gin.Context) {
	var recordPanFileParam validate.RecordPanFileParam

	err := ctx.ShouldBindJSON(&recordPanFileParam)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	resData, err := service.RecordUploadFile(recordPanFileParam)
	if err != nil {
		// 插入失败
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": resData}, "success")
}

// 外包生成分享连接，走web api
func GenerateShareUrl(ctx *gin.Context) {
	var generateShareUrl validate.GenerateShareUrl

	err := ctx.ShouldBindJSON(&generateShareUrl)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	ps, err := service.GenerateShareUrl(generateShareUrl, 1, 0)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": ps}, "Baidu account login success.")

}

// 通过分享id 转存到当前用户网盘 获取文件下载链接
func TransferShareFile(ctx *gin.Context) {
	var v validate.TransferShareFile

	err := ctx.ShouldBindJSON(&v)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	panUser, err := service.FindPanUserById(v.PanUserId)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	panFiles, err := service.SelectPanFilesByFromShareId(v.PanShareId, panUser.Id, 0)
	var result []model.PanFile
	if err == nil && len(panFiles) > 0 {
		result, err = service.GetPanFileDownloadLinks(panFiles, panUser)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}
	} else {
		panShare := model.PanShare{}
		err = model.FindById(&panShare, v.PanShareId, nil)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		// 百度user
		pcsConfig := pcs.GetBaiduPcsConfig()
		baiduUserInfo, err := pcsConfig.SwitchUser(&pcsconfig.BaiduBase{
			UID:  0,
			Name: panUser.BaiduName,
		})
		if err != nil {
			response.FailWithCode(ctx, nil, err.Error(), -4000012)
			return
		}

		if !strings.HasPrefix(v.RelativePath, "/") || !strings.HasSuffix(v.RelativePath, "/") {
			response.Fail(ctx, nil, "relative_path should start with /,and end with /")
			return
		}

		// 转存文件
		result, err = service.TransferShareFile(panShare, baiduUserInfo, v.RelativePath, panUser)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}
	}

	response.Success(ctx, gin.H{"data": result}, "success.")
	return
}

// 生成文件下载链接
func GenerateDownloadUrl(ctx *gin.Context) {
	var getPanFileIdFilter validate.GetPanFileIdFilter
	err := ctx.ShouldBindJSON(&getPanFileIdFilter)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 获取当前账户 Access Token
	accessToken, err := service.GetAccessTokenById(getPanFileIdFilter.UserId)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 获取当前文件fs_id
	Fsid, err := service.FindPanFileById(getPanFileIdFilter.Id)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 获取文件下载链接
	downloadData, err := baidu.GetPanFileDownloadUrl(accessToken, []uint64{Fsid})

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 拼接合法下载链接
	url := downloadData.List[0].Dlink + "&access_token=" + accessToken

	response.Success(ctx, gin.H{"url": url}, "Dlink is valid for 8 hours")

}

// 扫码登陆百度网盘
func ScanCodeAndLogin(ctx *gin.Context) {

	var sharePanAuthParam validate.SharePanAuthParam
	err := ctx.ShouldBindJSON(&sharePanAuthParam)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	wd, err := chrome.GetChromeBrowser()
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	// 这里都是同步请求  wd 在请求结束后回收就可以了
	defer wd.Quit()
	err = wd.Get("https://pan.baidu.com/")

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	btn, err := service.GetElementWithRetryTimes(&wd, "#TANGRAM__PSP_4__footerQrcodeBtn", 10)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	_ = btn.Click()

	// 获取二维码地址

	qrcodeElement, err := service.GetPcLoginQrCode(&wd, 60)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 下载二维码
	qrcodePath, _ := qrcodeElement.GetAttribute("src")
	q := service.QrCodeLogin{}
	q.InitClient()
	_, err = q.DownloadQrCode(qrcodePath, sharePanAuthParam.Name)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 同步查询二维码登录cookies
	cookies, err := service.QueryQrCodedLoginCookies(&wd, 1200, "")
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	if len(cookies) == 0 {
		response.Fail(ctx, nil, "can`t got cookies")
		return
	}
	cookiesStr := ""
	for _, cookie := range cookies {
		cookiesStr += cookie.Name + "=" + cookie.Value + ";"
	}
	cookiesStr = strings.TrimRight(cookiesStr, ";")
	_, _ = pcs.Login("", "", "", cookiesStr)

	// 使用cookies 进行登录
	baiduUserInfo, err := pcs.Login("", "", "", cookiesStr)

	if err != nil {
		fmt.Println("error is:", err.Error())
		return
	}

	// 移除图片缓存
	_ = os.Remove(qrcodePath)

	response.Success(ctx, gin.H{"data": baiduUserInfo}, "Baidu account login success.")
}

// 获取所有已经注册分享的账户
func GetShareLoginList(ctx *gin.Context) {
	accounts := pcs.LoginList()
	response.Success(ctx, gin.H{"accounts": accounts}, "All share account.")
}

// 获得登录二维码
func GetLoginQrCode(ctx *gin.Context) {
	var sharePanAuthParam validate.SharePanAuthParam
	err := ctx.ShouldBindJSON(&sharePanAuthParam)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	CacheName := teamones_helper.GenerateMd5(teamones_helper.StrVal(sharePanAuthParam.UnionId) + sharePanAuthParam.Name)

	// 如果存在进行中的tab 就close掉
	if cv, flag := cache.CacheHelper.Get("progressing" + CacheName); flag && cv == "yes" {
		cache.CacheHelper.Set("close_"+CacheName, "yes", 30*time.Second)
	}

	wd, err := chrome.GetChromeBrowser()
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	err = wd.Get("https://pan.baidu.com/")

	if err != nil {
		_ = wd.Quit()
		response.Fail(ctx, nil, err.Error())
		return
	}
	btn, err := service.GetElementWithRetryTimes(&wd, "#TANGRAM__PSP_4__footerQrcodeBtn", 50)

	if err != nil {
		_ = wd.Quit()
		response.Fail(ctx, nil, err.Error())
		return
	}

	_ = btn.Click()

	// 获取二维码地址

	qrcodeElement, err := service.GetPcLoginQrCode(&wd, 60)

	if err != nil {
		_ = wd.Quit()
		response.Fail(ctx, nil, err.Error())
		return
	}

	qrcodePath, _ := qrcodeElement.GetAttribute("src")

	// 清理掉之前的cookies
	cache.CacheHelper.Delete(CacheName)
	// 开启协程异步处理 登录后的cookies 存入Cache
	go service.QueryQrCodedLoginCookiesAndSetCacheCookies(&wd, 1200, CacheName)

	response.Success(ctx, gin.H{"data": map[string]interface{}{
		"qrcode_path": qrcodePath,
		"cache_name":  CacheName,
	}}, "Baidu account login success.")

}

// 获得登录信息
func GetLoginData(ctx *gin.Context) {
	var v validate.GetLoginData
	err := ctx.ShouldBindJSON(&v)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	cookiesStr, flag := cache.CacheHelper.Get(v.CacheName)
	if flag {
		baiduUserInfo, err := pcs.Login("", "", "", teamones_helper.StrVal(cookiesStr))
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}
		panUser := service.FindPanUserByBaiduName(baiduUserInfo.Name)
		if panUser.Id > 0 {
			//service.UpdatePanUserCookiesExpireAt(&panUser)
			service.UpdatePanUserCookies(panUser.Id, teamones_helper.StrVal(cookiesStr))
		}
		//qrcodePath := viper.GetString("server.staticPath") + "qrcode/" + v.QrcodeName + ".png"

		//_ = os.Remove(qrcodePath)
		response.Success(ctx, gin.H{"data": baiduUserInfo}, "Baidu account login success.")

		return

	} else {
		response.Success(ctx, nil, "query cache cookies failure")
		return
	}
}

// 获得panUser 通过unionId
func GetPanUserByUnionId(ctx *gin.Context) {

	userId := common.GetUInt64FromContext(ctx, "user_id")

	user, err := service.FindPanUserByUnionId(userId)

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.Success(ctx, nil, "success.")
			return
		}
		response.Fail(ctx, nil, err.Error())
		return
	}
	response.Success(ctx, gin.H{"data": user}, "success.")
	return
}

// PanFileManage 文件管理
func PanFileManage(ctx *gin.Context) {
	var v validate.PanFileManage
	err := ctx.ShouldBindJSON(&v)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	user, err := service.FindPanUserById(v.PanUserId)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	resp, err := baidu.FileManage(user.AccessToken, v.Opera, v.Async, v.FileList, v.Ondup)

	if err != nil {

		response.Fail(ctx, nil, err.Error())
		return
	}
	service.PanFileManage(v, resp)
	response.Success(ctx, gin.H{"data": resp}, "success.")
	return
}

// 重新生成分享链接
func RegenerateShare(ctx *gin.Context) {
	var v validate.RegenerateShare
	err := ctx.ShouldBindJSON(&v)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	share, err := service.FindPanShareById(v.ShareId)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	res, err := service.RegenerateShare(share)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	response.Success(ctx, gin.H{"data": res}, "success.")
	return
}

// SelectBoundPanUser 获得网盘用户
func SelectBoundPanUser(ctx *gin.Context) {
	var v validate.SelectBoundPanUser
	err := ctx.ShouldBindJSON(&v)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	userList, err := service.SelectPanUserByLinkData(v.UnionId, v.LinkType)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	response.Success(ctx, gin.H{"data": userList}, "success.")
	return
}

// SetDefaultUser 设置默认网盘用户
func SetDefaultUser(ctx *gin.Context) {
	var v validate.SetDefaultUser
	err := ctx.ShouldBindJSON(&v)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	err = service.SetDefaultUser(v.PanUserId)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": true}, "success.")
	return
}

// 获得panUser 通过id
func GetPanUserById(ctx *gin.Context) {

	var v validate.GetPanUserById
	err := ctx.ShouldBindJSON(&v)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	user, err := service.FindPanUserById(v.PanUserId)

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.Success(ctx, nil, "success.")
			return
		}
		response.Fail(ctx, nil, err.Error())
		return
	}
	response.Success(ctx, gin.H{"data": user}, "success.")
	return
}

// GetCurrentDefaultPanUser 查询默认网盘用户
func GetCurrentDefaultPanUser(ctx *gin.Context) {

	var v validate.GetCurrentDefaultPanUser
	err := ctx.ShouldBindJSON(&v)

	var unionId uint64 = 0
	switch v.LinkType {
	case "tenant":
		unionId = common.GetUInt64FromContext(ctx, "tenant_id")
		break
	case "user":
		unionId = common.GetUInt64FromContext(ctx, "user_id")
		break
	}

	user, err := service.GetDefaultPanUser(unionId, v.LinkType)

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.Success(ctx, nil, "success.")
			return
		}
		response.Fail(ctx, nil, err.Error())
		return
	}
	response.Success(ctx, gin.H{"data": user}, "success.")
	return
}
